home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************/
- /* routines to render a fractal landscape as an image */
- /************************************************************************/
-
- #include <proto/dos.h>
-
- #include <m68881.h>
- #include <math.h>
- #include <stdlib.h>
- #include <string.h>
-
- #include "paint.h"
- #include "crinkle.h"
- #include "global.h"
-
- static Col get_col( Height, Height, Height, Height );
- static int project( int, Height );
-
-
- #define SIDE 1.0
-
- double vstrength; // strength of vertical light source
- double lstrength; // strength of vertical light source
- int base; // parity flag for mirror routine
-
- Parm fold_param;
-
- /* -------------------------------------------------------------------- */
- /* setup the colour lookup table */
- /* -------------------------------------------------------------------- */
-
- void set_clut( int max_col, Gun *red, Gun *green, Gun *blue )
- {
- int band, shade;
- double top, bot;
- double intensity;
- int tmp;
-
- /* ---------------------------------------------------------------- */
- /* double rb[N_BANDS] = { 0.167,0.200,0.333,0.450,0.600,1.000 }; */
- /* double gb[N_BANDS] = { 0.667,0.667,0.500,0.500,0.600,1.000 }; */
- /* double bb[N_BANDS] = { 0.500,0.450,0.333,0.200,0.000,1.000 }; */
- /* ---------------------------------------------------------------- */
-
- double rb[N_BANDS];
- double gb[N_BANDS];
- double bb[N_BANDS];
-
- /* ---------------------------------------------------------------- */
- /* band base colours as RGB fractions */
- /* ---------------------------------------------------------------- */
-
- rb[0] = 0.450; rb[1] = 0.600; rb[2] = 1.000;
- gb[0] = 0.500; gb[1] = 0.600; gb[2] = 1.000;
- bb[0] = 0.333; bb[1] = 0.000; bb[2] = 1.000;
-
- red[BLACK] = 0; // black
- green[BLACK] = 0;
- blue[BLACK] = 0;
-
- red[WHITE] = COL_RANGE; // white
- green[WHITE] = COL_RANGE;
- blue[WHITE] = COL_RANGE;
-
- red[SKY] = 0.404 * COL_RANGE; // sky
- green[SKY] = 0.588 * COL_RANGE;
- blue[SKY] = COL_RANGE;
-
- red[SEA_LIT] = 0; // sea (lit)
- green[SEA_LIT] = 0.500 * COL_RANGE;
- blue[SEA_LIT] = 0.700 * COL_RANGE;
-
- red[SEA_UNLIT] = 0; // sea (unlit)
- green[SEA_UNLIT] = ((ambient+(vfract/(1.0+vfract)))*0.500)*COL_RANGE;
- blue[SEA_UNLIT] = ((ambient+(vfract/(1.0+vfract)))*0.700)*COL_RANGE;
-
- if( MIN_COL > max_col ) {
- PutStr( "set_clut: less than the minimum number of colours available\n" );
- exit( 1 );
- }
-
- /* ---------------------------------------------------------------- */
- /* max_col can over-rule band_size */
- /* ---------------------------------------------------------------- */
-
- while( BAND_BASE + band_size * N_BANDS > max_col ) {
- band_size--;
- }
-
- for ( band = 0; band<N_BANDS; band++ ) {
- for( shade = 0; shade < band_size; shade++ ) {
- if( BAND_BASE + band * band_size + shade >= max_col ) {
- PutStr( "INTERNAL ERROR, overflowed clut\n" );
- exit( 1 );
- }
-
- /* -------------------------------------------------------- */
- /* set red */
- /* -------------------------------------------------------- */
-
- top = rb[band];
- bot = ambient * top;
- intensity = bot + ( shade * ( top - bot ) ) / ( band_size - 1 );
- tmp = COL_RANGE * intensity;
- if ( tmp < 0 ) {
- Printf( "set_clut: internal error: invalid code %ld\n", tmp );
- exit( 2 );
- }
- if( tmp > COL_RANGE ) {
- tmp = COL_RANGE;
- }
- red[BAND_BASE + band * band_size + shade] = tmp;
-
- /* -------------------------------------------------------- */
- /* set green */
- /* -------------------------------------------------------- */
-
- top = gb[band];
- bot = ambient * top;
- intensity = bot + ( shade * ( top - bot ) ) / ( band_size - 1 );
- tmp = COL_RANGE * intensity;
- if ( tmp < 0 ) {
- Printf( "set_clut: internal error: invalid code %ld\n", tmp );
- exit( 2 );
- }
- if( tmp > COL_RANGE ) {
- tmp = COL_RANGE;
- }
- green[BAND_BASE + band * band_size + shade] = tmp;
-
- /* -------------------------------------------------------- */
- /* set blue */
- /* -------------------------------------------------------- */
-
- top = bb[band];
- bot = ambient * top;
- intensity = bot + shade * ( top - bot ) / ( band_size - 1 );
- tmp = COL_RANGE * intensity;
- if ( tmp < 0 ) {
- Printf( "set_clut: internal error: invalid code %ld\n", tmp );
- exit( 2 );
- }
- if( tmp > COL_RANGE ) {
- tmp = COL_RANGE;
- }
- blue[BAND_BASE + band * band_size + shade] = tmp;
- }
- }
- }
-
- /* -------------------------------------------------------------------- */
- /* extract the table of heights from the Strip struct and discard the */
- /* rest of the struct. */
- /* -------------------------------------------------------------------- */
-
- Height *extract( Strip *s )
- {
- int i;
- Height *p;
-
- p = s->d;
- free( s );
- for( i = 0; i < width; i++ ) {
- p[i] = shift + vscale * p[i];
- }
- return p;
- }
-
- /* -------------------------------------------------------------------- */
- /* initialise the variables for the artist routines. */
- /* -------------------------------------------------------------------- */
-
- void init_artist_variables( void )
- {
- double dh, dd;
- int pwidth; // longest lengthscale for update
-
- width = ( 1 << levels ) + 1;
- pwidth = ( 1 << ( levels - stop ) ) + 1;
-
- /* ---------------------------------------------------------------- */
- /* make the fractal SIDE wide, this makes it easy to predict the */
- /* average height returned by calcalt. If we have stop != 0 then */
- /* make the largest update length = SIDE */
- /* ---------------------------------------------------------------- */
-
- cos_phi = cos( phi );
- sin_phi = sin( phi );
- tan_phi = tan( phi );
-
- x_fact = cos_phi * cos( alpha );
- y_fact = cos_phi * sin( alpha );
- vscale = stretch * pwidth; // have approx same height as fractal width
- // this makes each pixel SIDE=1.0 wide.
- // c.f. get_col
- delta_shadow = tan_phi / cos( alpha );
- shadow_slip = tan( alpha );
-
- /* ---------------------------------------------------------------- */
- /* guess the average height of the fractal */
- /* ---------------------------------------------------------------- */
-
- varience = vscale * pow( SIDE , 2.0 * fdim );
- shift *= varience;
- varience += shift;
-
- start = ( sealevel - shift ) / vscale; // always start at sealevel
-
- /* ---------------------------------------------------------------- */
- /* set the position of the view point */
- /* ---------------------------------------------------------------- */
-
- viewheight = altitude * width;
- viewpos = - distance * width;
-
- /* ---------------------------------------------------------------- */
- /* set viewing angle and focal length (vertical-magnification) */
- /* try mapping the bottom of the fractal to the bottom of the */
- /* screen. Try to get points in the middle of the fractal */
- /* to be 1 pixel high */
- /* ---------------------------------------------------------------- */
-
- dh = viewheight;
- dd = width / 2.0 - viewpos;
- focal = sqrt( dd * dd + dh * dh );
-
- #ifndef SLOPPY
- tan_vangle = ( sealevel - viewheight ) / viewpos;
- vangle = atan ( tan_vangle ) - atan( height / focal / 2.0 );
- #else
-
- /* ---------------------------------------------------------------- */
- /* we are making some horrible approximations to avoid trig funcs */
- /* ---------------------------------------------------------------- */
-
- tan_vangle = ( sealevel - viewheight ) / viewpos - height / 2.0 / focal;
- #endif
-
- fold_param.mean = mean;
- fold_param.rg1 = smooth & 4;
- fold_param.rg2 = smooth & 2;
- fold_param.rg3 = smooth & 1;
- fold_param.cross = cross;
- fold_param.force_front = slope;
- fold_param.force_back = 0;
- fold_param.forceval = forceheight;
- fold_param.mix = mix;
- fold_param.midmix = midmix;
- fold_param.fdim = fdim;
-
- top = make_fold( &fold_param, levels, stop, SIDE / pwidth );
-
- /* ---------------------------------------------------------------- */
- /* use first set of heights to set shadow value */
- /* ---------------------------------------------------------------- */
-
- shadow = extract( next_strip( top ) );
- a_strip = extract( next_strip( top ) );
- b_strip = extract( next_strip( top ) );
-
- /* ---------------------------------------------------------------- */
- /* initialise the light strengths */
- /* ---------------------------------------------------------------- */
-
- vstrength = vfract * contrast / ( 1.0 + vfract );
- lstrength = contrast / ( 1.0 + vfract );
- }
-
- /* -------------------------------------------------------------------- */
- /* calculate the colour of a point. */
- /* -------------------------------------------------------------------- */
-
- static Col get_col( Height p, Height p_minus_x, Height p_minus_y, Height shadow )
- {
- Height delta_x,
- delta_y,
- delta_x_sqr,
- delta_y_sqr,
- hypot_sqr;
-
- double norm, dshade;
- Height effective;
- Col result;
- int band, shade;
-
- /* ---------------------------------------------------------------- */
- /* if underwater */
- /* ---------------------------------------------------------------- */
-
- if ( p < sealevel ) {
- if( shadow > sealevel ) {
- return SEA_UNLIT;
- }
- else {
- return SEA_LIT;
- }
- }
-
- /* ---------------------------------------------------------------- */
- /* We have three light sources, one slanting in from the left one */
- /* directly from above and an ambient light. */
- /* For the directional sources illumination is proportional to the */
- /* cosine between the normal to the surface and the light. */
- /* */
- /* The surface contains two vectors */
- /* ( 1, 0, delta_x ) */
- /* ( 0, 1, delta_y ) */
- /* */
- /* The normal therefore is parallel to */
- /* ( -delta_x, -delta_y, 1 ) / sqrt( 1 + delta_x² + delta_y² ) */
- /* */
- /* For light parallel to ( cos_phi, 0, -sin_phi ) the cosine is */
- /* ( cos_phi * delta_x + sin_phi ) / sqrt(1 + delta_x² + delta_y²) */
- /* */
- /* For light parallel to */
- /* ( cos_phi * cos_alpha, cos_phi * sin_alpha, -sin_phi ) */
- /* the cosine is */
- /* cos_phi * cos_alpha * delta_x + cos_phi * sin_alpha * delta_y + sin_phi */
- /* ----------------------------------------------------------------------- */
- /* sqrt( 1 + delta_x² + delta_y² ) */
- /* */
- /* For vertical light the cosine is */
- /* 1 / sqrt( 1 + delta_x² + delta_y² ) */
- /* ---------------------------------------------------------------- */
-
- delta_x = p - p_minus_x;
- delta_y = p - p_minus_y;
- delta_x_sqr = delta_x * delta_x;
- delta_y_sqr = delta_y * delta_y;
- hypot_sqr = delta_x_sqr + delta_y_sqr;
- norm = sqrt( 1.0 + hypot_sqr );
-
- /* ---------------------------------------------------------------- */
- /* calculate effective height */
- /* ---------------------------------------------------------------- */
-
- effective = p + varience * contour / ( 1.0 + hypot_sqr );
-
- /* ---------------------------------------------------------------- */
- /* calculate colour band. */
- /* ---------------------------------------------------------------- */
-
- band = ( effective / varience ) * N_BANDS;
- if ( band < 0 ) {
- band = 0;
- }
- if( band > N_BANDS - 1 ) {
- band = N_BANDS - 1;
- }
- result = BAND_BASE + ( band * band_size );
-
- /* ---------------------------------------------------------------- */
- /* calculate the illumination stength */
- /* */
- /* add in a contribution for the vertical light. The normalisation */
- /* factor is applied later */
- /* ---------------------------------------------------------------- */
-
- dshade = vstrength;
-
- if( p >= shadow ) {
-
- /* ------------------------------------------------------------ */
- /* add in contribution from the main light source */
- /* ------------------------------------------------------------ */
-
- dshade += lstrength * ( delta_x * x_fact + delta_y * y_fact + sin_phi );
- }
-
- /* ---------------------------------------------------------------- */
- /* divide by the normalisation factor (the same for both light */
- /* sources) */
- /* ---------------------------------------------------------------- */
-
- dshade /= norm;
-
- /* ---------------------------------------------------------------- */
- /* calculate shading */
- /* */
- /* dshade should be in the range 0.0 -> 1.0 */
- /* if the light intensities add to 1.0 */
- /* now convert to an integer */
- /* ---------------------------------------------------------------- */
-
- shade = dshade * band_size;
- if ( shade > band_size - 1 ) {
- shade = band_size - 1;
- }
-
- /* ---------------------------------------------------------------- */
- /* if shade is negative then point is really in deep shadow */
- /* ---------------------------------------------------------------- */
-
- if( shade < 0 ) {
- shade = 0;
- }
-
- result += shade;
- if( result >= n_col || result < 0 ) {
- Printf( "INTERNAL ERROR colour out of range %ld\n", result );
- exit( 1 );
- }
- return result;
- }
-
-
- /* -------------------------------------------------------------------- */
- /* This routine returns a plan view of the surface */
- /* -------------------------------------------------------------------- */
-
- Col *makemap( Height *a, Height *b, Height *shadow )
- {
- Col *res;
- int i;
-
- res = (Col *) malloc( width * sizeof( Col ) );
- if ( res == NULL ) {
- PutStr( "malloc failed for colour strip\n" );
- exit( 1 );
- }
- res[0] = BLACK;
- for( i = 1; i < width; i++ ) {
- res[i] = get_col( b[i], a[i], b[i-1], shadow[i] );
- }
- return res;
- }
-
- /* -------------------------------------------------------------------- */
- /* this routine returns a perspective view of the surface */
- /* -------------------------------------------------------------------- */
-
- Col *camera( Height *a, Height *b, Height *shadow )
- {
- int i, coord, last;
- Col *res, col;
-
- res = (Col *) malloc( height * sizeof( Col ) );
- if( res == NULL ) {
- PutStr( "malloc failed for picture strip\n" );
- exit( 1 );
- }
-
- /* ---------------------------------------------------------------- */
- /* optimised painters algorithm */
- /* */
- /* scan from front to back, we can avoid calculating the */
- /* colour if the point is not visable. */
- /* ---------------------------------------------------------------- */
-
- for( i = last = 0 ; i < width && last < height; i++ ) {
- if( a[i] < sealevel ) {
- a[i] = sealevel;
- }
- coord = 1 + project( i, a[i] );
- if ( coord > last ) {
-
- /* -------------------------------------------------------- */
- /* get the colour of this point, the front strip should be */
- /* black */
- /* -------------------------------------------------------- */
-
- if ( i == 0 ) {
- col = BLACK;
- }
- else {
- col = get_col( b[i], a[i], b[i-1], shadow[i] );
- }
- if ( coord > height ) {
- coord = height;
- }
- for ( ; last<coord; last++ ) {
- res[last] = col;
- }
- }
- }
- for ( ; last < height; last++ ) {
- res[last] = SKY;
- }
- return res;
- }
-
- /* -------------------------------------------------------------------- */
- /* this routine returns a perspective view of the surface with */
- /* reflections in the water */
- /* -------------------------------------------------------------------- */
-
- Col *mirror( Height *a, Height *b, Height *shadow )
- {
- Col *res, *map;
- Col last_col;
- int i, j, top, bottom, coord;
- int last_top, last_bottom;
- Height pivot;
-
- res = (Col *) malloc( height * sizeof( Col ) );
- if( res == NULL ) {
- PutStr( "malloc failed for picture strip\n" );
- exit( 1 );
- }
- last_col = SKY;
- last_top = height - 1;
- last_bottom = 0;
-
- /* ---------------------------------------------------------------- */
- /* many of the optimisation in the camera routine are hard to */
- /* implement in this case so we revert to the simple painters */
- /* algorithm modified to produce reflections scan from back to */
- /* front drawing strips between the projected position of height */
- /* and -height. for water stipple the colour so the reflection is */
- /* still visable */
- /* ---------------------------------------------------------------- */
-
- map = makemap( a, b, shadow );
- pivot = 2.0 * sealevel;
- for ( i = width - 1; i > 0; i-- ) {
- if ( map[i] < BAND_BASE ) {
-
- /* -------------------------------------------------------- */
- /* stipple water values */
- /* -------------------------------------------------------- */
-
- for( j = last_bottom; j <= last_top; j++ ) {
- res[j] = last_col;
- }
- last_col = map[i];
-
- /* -------------------------------------------------------- */
- /* invalidate strip so last stip does not exist */
- /* -------------------------------------------------------- */
-
- last_bottom = height;
- last_top = -1;
-
- /* -------------------------------------------------------- */
- /* fill in water values */
- /* -------------------------------------------------------- */
-
- coord = 1 + project( i, sealevel );
- for ( j = 0; j < coord; j++ ) {
-
- /* ---------------------------------------------------- */
- /* do not print on every other point if the current */
- /* value is a land value */
- /* ---------------------------------------------------- */
-
- if ( ( j + base ) % 2 || res[j] < BAND_BASE ) {
- res[j] = map[i];
- }
- }
-
- /* -------------------------------------------------------- */
- /* skip any adjacent bits of water with the same colour */
- /* -------------------------------------------------------- */
-
- while ( map[i] == last_col ) {
- i--;
- }
- i++; // the end of the for loop will decrement as well
- }
- else {
-
- /* -------------------------------------------------------- */
- /* draw land values */
- /* -------------------------------------------------------- */
-
- top = project( i, a[i] );
- bottom = project( i, pivot - a[i] );
- if( last_col == map[i] ) {
- if( top > last_top ) {
- last_top = top;
- }
- if( bottom < last_bottom ) {
- last_bottom = bottom;
- }
- }
- else {
- if ( top < last_top ) {
- for ( j = top + 1; j <= last_top; j++ ) {
- res[j] = last_col;
- }
- }
- if ( bottom > last_bottom ) {
- for ( j = last_bottom; j < bottom; j++ ) {
- res[j] = last_col;
- }
- }
- last_top = top;
- last_bottom = bottom;
- last_col = map[i];
- }
- }
- }
-
- /* ---------------------------------------------------------------- */
- /* draw in front face */
- /* ---------------------------------------------------------------- */
-
- for ( j = last_bottom; j <= last_top; j++ ) {
- res[j] = last_col;
- }
- if( a[0] < sealevel ) {
- coord = 1 + project( 0, sealevel );
- }
- else {
- coord = 1 + project( 0, a[0] );
- }
- for( j = 0; j < coord; j++ ) {
- res[j] = map[0];
- }
-
- base = 1 - base;
- free( map );
- return res;
- }
-
- /* -------------------------------------------------------------------- */
- /* project a point onto the screen position */
- /* -------------------------------------------------------------------- */
-
- static int project( int x, Height y )
- {
- int pos;
- #ifndef SLOPPY
- double theta;
-
- theta = atan( ( viewheight - y ) / ( x - viewpos ) ) - vangle;
- pos = height / 2 - focal * tan( theta );
- #else
- double tan_theta;
-
- /* ---------------------------------------------------------------- */
- /* nast approx to avoid trig functions */
- /* ---------------------------------------------------------------- */
-
- tan_theta = ( viewheight - y ) / ( x - viewpos ) - tan_vangle;
- pos = height / 2 - focal * tan_theta;
- #endif
- if( pos > height - 1 ) {
- pos = height - 1;
- }
- else if ( pos < 0 ) {
- pos = 0;
- }
- return pos;
- }
-
- /* -------------------------------------------------------------------- */
- /* Tidy up and free everything. */
- /* -------------------------------------------------------------------- */
-
- void finish_artist( void )
- {
- free( a_strip );
- free( b_strip );
- free( shadow );
- free_fold( top );
- }
-